/*
 * Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache license, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.internna.iwebjtracker.model;

import java.util.Date;
import java.util.HashSet;
import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import org.directwebremoting.annotations.DataTransferObject;
import org.internna.iwebjtracker.activity.model.RecordedActivity.ActivityType;
import org.internna.iwebjtracker.issue.model.Issue.Link;
import org.internna.iwebjtracker.issue.model.Issue.ResolutionStatus;
import org.internna.iwebjtracker.issue.model.Issue.Severity;
import org.internna.iwebjtracker.issue.model.Issue.Status;
import org.internna.iwebjtracker.issue.model.Issue.Type;
import org.internna.iwebjtracker.project.validation.Errors;
import org.internna.iwebmvc.metadata.EntitySecurity;
import org.internna.iwebmvc.metadata.EntitySecurity.CRUD;
import org.internna.iwebmvc.metadata.EntitySecurityRule;
import org.internna.iwebmvc.model.Document;
import org.internna.iwebmvc.model.MultiDocument;
import org.internna.iwebmvc.model.security.UserImpl;

import static org.internna.iwebjtracker.boot.tasks.CreateRolesStartupTask.ROLE_ISSUE_CREATOR;
/**
 * An Issue implementation
 *
 * @author Jose Noheda
 * @since 1.0
 */
@Entity
@Table(name = "ISSUES")
@DataTransferObject(javascript = "DefaultIssue")
@NamedQueries({
    @NamedQuery(name = "DefaultIssue.findAll", query = "SELECT i FROM DefaultIssue i"),
    @NamedQuery(name = "DefaultIssue.findByModule", query = "SELECT i FROM DefaultIssue i WHERE parentModule = :parentModule"),
    @NamedQuery(name = "DefaultIssue.findByProject", query = "SELECT i FROM DefaultIssue i WHERE parentProject = :parentProject"),
    @NamedQuery(name = "DefaultIssue.findByVersion", query = "SELECT i FROM DefaultIssue i WHERE affectedVersion = :affectedVersion ORDER BY alias"),
    @NamedQuery(name = "DefaultIssue.findByTargetVersion", query = "SELECT i FROM DefaultIssue i WHERE targetVersion = :targetVersion ORDER BY alias")
})
@EntitySecurity({
    @EntitySecurityRule(operation = CRUD.CREATE, ifAnyGranted = {ROLE_ISSUE_CREATOR})
})
public class DefaultIssue extends IssueCollectionManager {

    private static final long serialVersionUID = -1829531327521346319L;

    /**
     * Named query to list all issues for a specific version or milestone.
     */
    public static final String FIND_BY_VERSION = "DefaultIssue.findByVersion";

    /**
     * Named query to list all issues with a specific target.
     */
    public static final String FIND_BY_TARGET_VERSION = "DefaultIssue.findByTargetVersion";

    /**
     * JQL to get the next valid alias to assign to an issue.
     */
    public static final String FIND_BY_CURRENT_ALIAS = "SELECT MAX(alias) + 1 FROM DefaultIssue WHERE parentProject.id = :project";

    /**
     * Creates a new activity to record the vote.
     *
     * @param user any
     * @return true
     */
    @Override protected final boolean onVote(TrackerUser user) {
        createActivity(ActivityType.ISSUE_VOTE, user);
        return true;
    }

    /**
     * Creates a new activity to record the action.
     *
     * @param user any
     * @return the result of the logic
     */
    @Override protected final boolean onWatch(TrackerUser user) {
        createActivity(ActivityType.ISSUE_WATCH, user);
        return true;
    }

    /**
     * Creates a new activity to record the comment.
     *
     * @param comment a non null instance
     * @return true
     */
    @Override protected final boolean onAddComment(IssueComment comment) {
        createActivity(ActivityType.ISSUE_COMMENT, comment.getAuthor());
        return true;
    }

    /**
     * Creates a new activity to record the attachment.
     *
     * @param md any
     * @param user any
     * @param doc any
     * @return
     */
    @Override protected final MultiDocument onAddAttachment(MultiDocument md, TrackerUser user, Document doc) {
        if (md != null) {
            createActivity(ActivityType.ISSUE_ATTACHMENT, user);
        }
        return md;
    }

    /**
     * Adds the link to the collection if able.
     *
     * @param linkedIssue any
     * @param type any
     * @return
     */
    @Override public Errors link(TrackerUser user, DefaultIssue linkedIssue, Link type) {
        if ((linkedIssue != null) && (type != null)) {
            if (links == null) {
                setLinks(new HashSet<Interlink>(1));
            }
            if (links.add(new Interlink(this, linkedIssue, type))) {
                createActivity(ActivityType.ISSUE_LINK, user);
                return null;
            }
            return Errors.ISSUE_LINK_EXISTED;
        }
        return Errors.MISSING_INFO;
    }

    /**
     * Changes the status and generates and activity.
     *
     * @param user any
     * @param status any (if null it defaults to FIXED)
     * @return any
     */
    @Override public boolean close(TrackerUser user, ResolutionStatus status) {
        if ((user != null) && (!Status.CLOSED.equals(getStatus()))) {
            setStatus(Status.CLOSED);
            if (getAssignee() == null) {
                setAssignee(user);
            }
            setFixVersion(getParentProject().getCurrentVersion());
            setResolutionStatus(status == null ? ResolutionStatus.FIXED : status);
            createActivity(ActivityType.ISSUE_CLOSE, user);
        }
        return user != null && Status.CLOSED.equals(getStatus());
    }

    /**
     * Fills the activity with a reference to this issue.
     *
     * @param activity a non null activity
     */
    @Override protected void onCreateActivity(Activity activity) {
        activity.setIssue(this);
    }

    /**
     * Invoked automatically by the persistence provider to keep track of changes.
     * Avoid calling this method.
     */
    @PreUpdate
    protected final void preUpdate() {
        setLastModified(new Date());
    }

    /**
     * Invoked by the persistence provider to add default values and entity security automatically.
     * Avoid calling this method.
     */
    @PrePersist
    protected final void prePersist() {
        setSecurity();
        setDefaults();
    }

    private void setSecurity() {
        setPublicView(getParentProject().isPublicView());
        for (UserImpl user : getParentProject().getOwners()) {
            addOwner(user);
        }
        if (!isPublicView()) {
            for (UserImpl user : getParentProject().getViewers()) {
                addViewer(user);
            }
        }
    }

    private void setDefaults() {
        setType(getType() == null ? Type.TASK.BUG : getType());
        setSeverity(getSeverity() == null ? Severity.FIX : getSeverity());
        setResolutionStatus(getResolutionStatus() == null ? ResolutionStatus.PENDING : getResolutionStatus());
        setStatus(getStatus() == null ? Status.OPEN : getStatus());
        if (getCreationDate() == null) {
            setCreationDate(new Date());
        }
    }

}
